package com.nulldev.util.crypto.api2.encryptors;

import java.nio.charset.Charset;
import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.nulldev.util.crypto.api2.Cryptable;
import com.nulldev.util.java.JVMUtil;

public class AES extends Cryptable{

	private final String TYPE = "AES";
	public AES() {
		super("AES");
	}

	@Override
	public String encrypt(String object, String pass) {
		return this.encrypt(object, pass, null);
	}

	@Override
	public String decrypt(String object, String pass) {
		return this.decrypt(object, pass, null);
	}
	
	@Override
	public String encrypt(String object, String password, Charset charset) {
		try {
			final StringBuilder pword = new StringBuilder(password);
			while (pword.length() % 16 != 0) {
				pword.append("X");
			}
//			if (pass.length() % 8 != 0 || pass.length() > Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding")) {
//				throw new RuntimeException("Password is not an interval of 8 or is too large. Got '" + pass.length() + "'.");
//			}
			final String output = pword.toString();
		    Key aesKey = new SecretKeySpec(output.getBytes(), this.TYPE);
		    Cipher cipher = Cipher.getInstance(this.TYPE);
		    try {
		    	cipher.init(Cipher.ENCRYPT_MODE, aesKey);
		    }catch (Exception e) {
		    	cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(new byte[16]));
		    }
		    if (charset == null) {
			    final String out = toHexString(cipher.doFinal(object.getBytes()));
			    return out;
		    }else {
			    final String out = toHexString(cipher.doFinal(object.getBytes(charset)));
			    return out;
		    }
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public String decrypt(String object, String password, Charset charset) {
		try {
			final StringBuilder pword = new StringBuilder(password);
			while (pword.length() % 16 != 0) {
				pword.append("X");
			}
			final String output = pword.toString();
		    Key aesKey = new SecretKeySpec(output.getBytes(), this.TYPE);
		    Cipher cipher = Cipher.getInstance(this.TYPE);
		    try {
		    	cipher.init(Cipher.DECRYPT_MODE, aesKey);
		    }catch (Exception e) {
		    	cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(new byte[16]));
		    }
		    if (charset == null) {
		    	return new String(cipher.doFinal(toByteArray(object)));
		    }else {
		    	return new String(cipher.doFinal(toByteArray(object)), charset);
		    }
		}catch (Exception e) {
			JVMUtil.printError(e);
			throw new RuntimeException("Decryption failed.");
		}
	}

    public static String toHexString(byte[] bytes) {
    	StringBuilder sb = new StringBuilder();
     	for (byte b : bytes) {
     		sb.append(String.format("%02X", b));
     	}
     	return sb.toString();
    }

    public static byte[] toByteArray(String s) {
    	final int len = s.length();
     	final byte[] data = new byte[len / 2];
     	for (int i = 0; i < len; i += 2) {
     		data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
     	}
     	return data;
    }

}
